/***************************************************************************
 *  This is the source code of Maitreya, open source platform
 *  for Vedic and western astrology.

 *  File       src/jyotish/SimpleHoroscope.cpp
 *  Release    6.0
 *  Author     M. Pettau
 *  Copyright  2003-2010 by the author

 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
****************************************************************************/

#ifdef __GNUG__
  #pragma implementation "SimpleHoroscope.h"
#endif

#include "SimpleHoroscope.h"

#include "Calculator.h"
#include "Conf.h"
#include "constants.h"
#include "mathbase.h"

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <wx/string.h>

extern Config *config;

IMPLEMENT_CLASS( SimpleHoroscope, wxEvtHandler )

/*****************************************************
**
**   ObjectPosition   ---   Constructor
**
******************************************************/
ObjectPosition::ObjectPosition()
{
  length = 0;
  latitude = 0;
  speed = 0;
  retro = false;
}

/*****************************************************
**
**   ObjectPosition   ---   Constructor
**
******************************************************/
ObjectPosition::ObjectPosition( const double &l, const double &b, const double &s )
{
  length = l;
  latitude = b;
  speed = s;
  retro = false;
}

/*****************************************************
**
**   SimpleHoroscope   ---   Constructor
**
******************************************************/
SimpleHoroscope::SimpleHoroscope()
{
  int i;
  for ( i = 0; i < MAX_EPHEM_OBJECTS; i++ )
  {
    object_len[i] = 0;
    object_lat[i] = 0;
    object_speed[i] = 1;
  }
  for( i = HOUSE1; i <= HOUSE12; i++ ) whousecusp[i] = ihousecusp[i] = ihousesandhi[0] = 0;

  dataSet = new FileDataSet;
  dataSet->setLocation( config->defaultLocation );
}

/*****************************************************
**
**   SimpleHoroscope   ---   Destructor
**
******************************************************/
SimpleHoroscope::~SimpleHoroscope()
{
  delete dataSet;
}

/*****************************************************
**
**   SimpleHoroscope   ---   updatePlanets
**
** update of Ayanamsa, object_len etc and house length
**
******************************************************/
void SimpleHoroscope::updatePlanets()
{
  int i;
  double r[12], houselen;
  Calculator *calculator = CalculatorFactory().getCalculator();

  // Ayanamsa
  iayanamsa_type = config->vAyanamsa;
  wayanamsa_type = config->wAyanamsa;
  iayanamsa = calculator->calcAyanamsa( getJD(), iayanamsa_type );
  wayanamsa = calculator->calcAyanamsa( getJD(), wayanamsa_type );

  // Planetary positions
  for( i = 0; i < MAX_EPHEM_OBJECTS; i++ )
  {
    if ( i == OMEANDESCNODE || i == OTRUEDESCNODE || i == OASCENDANT || i == OMERIDIAN ) continue;
    calculator->calcPositionSpeed( dataSet, i, object_len[i], object_lat[i], object_speed[i] );
  }
  object_len[OMEANDESCNODE] = red_deg( object_len[OMEANNODE] + 180 );
  object_len[OTRUEDESCNODE] = red_deg( object_len[OTRUENODE] + 180 );
  object_speed[OMEANDESCNODE] = object_speed[OMEANNODE];
  object_speed[OTRUEDESCNODE] = object_speed[OTRUENODE];

  // House calculation
  calculator->calcHouses( getJD(), getLongitude(), getLatitude(), config->wHouseSystem, r );
  for( i = HOUSE1; i <= HOUSE12; i++ ) whousecusp[i] = red_deg( r[i] - wayanamsa );

  if ( config->vHouseSystem != config->wHouseSystem )
    calculator->calcHouses( getJD(), getLongitude(), getLatitude(), config->vHouseSystem, r );

  for( i = HOUSE1; i <= HOUSE12; i++ ) ihousecusp[i] = red_deg( r[i] - iayanamsa );
  for( i = HOUSE1; i <= HOUSE12; i++ )
  {
    houselen = red_deg( ihousecusp[i<11 ? i+1 : 0] - ihousecusp[i] );
    ihousesandhi[i] = red_deg( ihousecusp[i] + .5 * houselen );
  }
  object_len[OASCENDANT] = r[0];
  object_len[OMERIDIAN] = r[9];

  siderealTime = calculator->calcSiderealTime( getJD(), getLongitude() );

  updatePositionArrays();
}

/*****************************************************
**
**   SimpleHoroscope   ---   updatePositionArrays
**
** updates the arrays vpos and wpos
** integrated in updatePlanets, only once called by TransitHoroscope
**
******************************************************/
void SimpleHoroscope::updatePositionArrays()
{
  // Setup Object position structs
  for( int i = 0; i < MAX_EPHEM_OBJECTS; i++ )
  {
    vpos[i].length   = wpos[i].length   = object_len[i];
    vpos[i].latitude = wpos[i].latitude = object_lat[i];
    vpos[i].speed    = wpos[i].speed    = object_speed[i];
    if ( i != OMEANNODE && i != OMEANDESCNODE )
      vpos[i].retro = wpos[i].retro = ( object_speed[i] < 0 );

    // p.length =0 can happen for planetoids etc. without data files.
    // Do not adjust Ayanamsa in that case, so 0 Aries position will be quite obvious.
    if ( object_len[i] != 0 )
    {
      vpos[i].length = red_deg( vpos[i].length - iayanamsa );
      wpos[i].length = red_deg( wpos[i].length - wayanamsa );
    }
  }
}

/*****************************************************
**
**   SimpleHoroscope   ---   getObjectPosition
**
******************************************************/
ObjectPosition SimpleHoroscope::getObjectPosition( const int &planet, const bool &vedic ) const
{
  ASSERT_VALID_EPHEM_OBJECT( planet )
  return( vedic ? vpos[planet] : wpos[planet] );
}

/*****************************************************
**
**   SimpleHoroscope   ---   getLength
**
******************************************************/
double SimpleHoroscope::getLength( const int &planet, const bool &vedic ) const
{
  ASSERT_VALID_OBJECT( planet );
  return getObjectPosition( planet, vedic ).length;
}

/*****************************************************
**
**   SimpleHoroscope   ---   isRetrograde
**
******************************************************/
bool SimpleHoroscope::isRetrograde( const int &planet ) const
{
  ASSERT_VALID_OBJECT( planet );
  return getObjectPosition( planet, true ).retro;
}

/*****************************************************
**
**   SimpleHoroscope   ---   getTropicalLength
**
******************************************************/
double SimpleHoroscope::getTropicalLength( const int &planet ) const
{
  ASSERT_VALID_EPHEM_OBJECT( planet )
  return object_len[planet];
}

/*****************************************************
**
**   SimpleHoroscope   ---   getLatitude
**
******************************************************/
double SimpleHoroscope::getLatitude( const int &planet ) const
{
  ASSERT_VALID_OBJECT( planet );
  return getObjectPosition( planet, true ).latitude;
}

/*****************************************************
**
**   SimpleHoroscope   ---   getSpeed
**
******************************************************/
double SimpleHoroscope::getSpeed( const int &planet ) const
{
  ASSERT_VALID_EPHEM_OBJECT( planet )
  return object_speed[planet];
}

/*****************************************************
**
**   SimpleHoroscope   ---   getHouse
**
******************************************************/
double SimpleHoroscope::getHouse( const int& housenb, const bool& vedic, const bool getsandhi ) const
{
  ASSERT_VALID_HOUSE( housenb )
  if( vedic ) { return( getsandhi ? ihousesandhi[housenb] : ihousecusp[housenb] ); }
  else { return whousecusp[housenb]; }
}

/*****************************************************
**
**   SimpleHoroscope   ---   getHouseForLength
**
******************************************************/
double SimpleHoroscope::getHouse4Length( const double &len, const bool &vedic ) const
{
  double hstart, hend, hlen, hpart, ret;
  for( int i = HOUSE1; i <= HOUSE12; i++ )
  {
    if ( ! vedic )
    {
      hstart = whousecusp[i];
      hend = whousecusp[red12(i+1)];
    }
    else
    {
      if ( config->vHouseWestern )
      {
        hstart = ihousecusp[i];
        hend = ihousecusp[red12(i+1)];
      }
      else
      {
        hstart = ihousesandhi[red12( i -1)];
        hend = ihousesandhi[red12(i)];
      }
    }
    if (( hstart <= len && len < hend ) || ( hstart > hend && ( len >= hstart || len < hend )))
    {
      hlen = red_deg( hend - hstart );
      hpart = red_deg( len - hstart );
      ret = i + hpart / hlen;
      //printf( "SimpleHoroscope::getHouse4Length len %f vedic %d hstart %f hend %f hlen %f hpart %f ret %f\n", len, vedic, hstart, hend, hlen, hpart, ret );
      assert( ret >= 0 && ret < 12 );
      return ret;
    }
  }
  assert( 0 );

return 0;
}

#include <QDebug>
#include "Lang.h"
/*****************************************************
**
**   SimpleHoroscope   ---   getHousePos
**
******************************************************/
int SimpleHoroscope::getHousePos( const int &planet, const bool &vedic ) const
{
  // TODO store house lengths on update() and return that instead of repeated calculation
  qDebug() << planet << Lang().getObjectName( planet, TLARGE, vedic ).ToAscii().data()<<" = " << getHouse4Length( getLength( planet, vedic), vedic );
  return (int)getHouse4Length( getLength( planet, vedic), vedic );
}

/*****************************************************
**
**   SimpleHoroscope   ---   openFile
**
******************************************************/
bool SimpleHoroscope::openFile( const wxChar *filename )
{
  return dataSet->openFile( filename );
}

/*****************************************************
**
**   SimpleHoroscope   ---   saveAs
**
******************************************************/
bool SimpleHoroscope::saveAs( const wxChar *filename )
{
  return dataSet->saveAs( filename );
}

/*****************************************************
**
**   SimpleHoroscope   ---   save
**
******************************************************/
bool SimpleHoroscope::save()
{
  return dataSet->save();
}


